package cn.ava.ymcc.service.impl;

import cn.ava.exception.GlobleException;
import cn.ava.ymcc.constants.CommonConstants;
import cn.ava.ymcc.domain.AlipayInfo;
import cn.ava.ymcc.domain.PayFlow;
import cn.ava.ymcc.domain.PayOrder;
import cn.ava.ymcc.dto.AlipayNotifyDto;
import cn.ava.ymcc.dto.Order2PayDto;
import cn.ava.ymcc.dto.PayNotifyDto;
import cn.ava.ymcc.dto.PayOrderParamDto;
import cn.ava.ymcc.mapper.PayOrderMapper;
import cn.ava.ymcc.service.IAlipayInfoService;
import cn.ava.ymcc.service.IPayFlowService;
import cn.ava.ymcc.service.IPayOrderService;
import cn.ava.ymcc.utils.AssertUtil;
import cn.ava.ymcc.utils.PayUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.payment.common.models.AlipayTradeCloseResponse;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.LocalTransactionState;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author ava
 * @since 2022-08-19
 */
@Service
@Slf4j
public class PayOrderServiceImpl extends ServiceImpl<PayOrderMapper, PayOrder> implements IPayOrderService {

    @Autowired
    private IAlipayInfoService alipayInfoService;

    @Autowired
    private IPayFlowService payFlowService;

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    /**
     * 保存支付单
     * @param payDto
     */
    @Override
    public void savaCoursePayOrder(Order2PayDto payDto) {
        Wrapper<PayOrder> query = new EntityWrapper<>();
        query.eq("order_no",payDto.getOrderNo());
        PayOrder payOrder = selectOne(query);
        // 支付单为空才进行保存
        if (payOrder == null) {
            payOrder = new PayOrder();
            BeanUtils.copyProperties(payDto,payOrder);
            payOrder.setCreateTime(new Date());
            payOrder.setExtParams(payDto.getExtParams());
            payOrder.setPayStatus(CommonConstants.OrderPayState.STATE_WAIT);
            insert(payOrder);
        }
    }

    /**
     * 轮询校验订单号,查询支付单是否存在,发起支付请求
     * @param orderNo
     */
    @Override
    public PayOrder checkPayOrder(String orderNo) {
        Wrapper<PayOrder> query = new EntityWrapper<>();
        query.eq("order_no",orderNo);
        return selectOne(query);
    }

    /**
     * 调用支付接口发起支付请求
     * @param paramDto
     */
    @Override
    public String apply(PayOrderParamDto paramDto) {
        // 校验支付单是否存在
        PayOrder payOrder = checkPayOrder(paramDto.getOrderNo());
        AssertUtil.isNotNull(payOrder,"支付单不存在!");
        // 校验支付单状态是否正确
        AssertUtil.isTrue(CommonConstants.OrderPayState.STATE_WAIT.equals(payOrder.getPayStatus()),
                "支付状态异常!");
        // 判断支付类型
        if (CommonConstants.OrderPayType.PAY_TYPE_ALIPAY.equals(payOrder.getPayType())) {
            log.info("调用支付宝发起支付");
            // 查询出支付宝公钥私钥
            AlipayInfo alipayInfo = alipayInfoService.selectList(null).get(0);
            // 调用支付宝支付
            return PayUtil.alipay(payOrder, paramDto,alipayInfo);
        }else if (CommonConstants.OrderPayType.PAY_TYPE_WECHATPAY.equals(payOrder.getPayType())){
            // 微信支付接口
        }else if (CommonConstants.OrderPayType.PAY_TYPE_UNIONPAY.equals(payOrder.getPayType())){
            // 银联支付接口
        }else if (CommonConstants.OrderPayType.PAY_TYPE_BALANCE.equals(payOrder.getPayType())){
            // 站内余额支付
        }
        return null;
    }


    /**
     *  支付宝异步返回通知
     * @param dto
     */
    @Override
    @Transactional
    public String AlipayNotify(AlipayNotifyDto dto) {
        log.info("支付宝返回异步通知!");
        // 校验状态是否成功,不成功就不用往下执行了
        AssertUtil.isTrue(dto.isTradeSuccess(),"交易失败");
        // 转换为map,接收验证签名
        Map map = JSONObject.parseObject(JSONObject.toJSONString(dto), Map.class);
        try {
            Boolean notify = Factory.Payment.Common().verifyNotify(map);
            if (!notify) { // 如果返回false,说明此消息已经失败了
                log.info("异步通知消息失败!");
                return "SUCCESS"; // 返回成功,不用继续通知了
            }
        } catch (Exception e) {
            e.printStackTrace();
            return "FAIL"; // 出现异常,继续通知
        }
        // 业务校验
        PayOrder payOrder = checkPayOrder(dto.getOut_trade_no());
        if (payOrder == null){
            // 订单不存在
            return "SUCCESS"; // 直接结束通知
        }
        if (payOrder.getAmount().compareTo(new BigDecimal(dto.getTotal_amount())) != 0) {
            // 金额不相等
            return "SUCCESS"; // 直接结束通知
        }
        if (!CommonConstants.OrderPayState.STATE_WAIT.equals(payOrder.getPayStatus())) {
            // 状态不为待支付
            try {
                // 使用支付宝执行退款
                Factory.Payment.Common().refund(payOrder.getOrderNo(),payOrder.getAmount().toString());
                log.info("调用支付宝执行退款流程");
            } catch (Exception e) {
                e.printStackTrace();
                // 退款异常,通知运维人工处理
            }
            // 结束通知
            return "SUCCESS";
        }
        // 修改支付单状态
        payOrder.setPayStatus(CommonConstants.OrderPayState.STATE_SUCCESS);
        Date now = new Date();
        payOrder.setUpdateTime(now);
        // 保存支付流水
        PayFlow flow = new PayFlow();
        flow.setNotifyTime(now);
        flow.setSubject(payOrder.getSubject());
        flow.setOutTradeNo(dto.getOut_trade_no());
        flow.setTotalAmount(new BigDecimal(dto.getTotal_amount()));
        flow.setTradeStatus(dto.getTrade_status());
        flow.setCode(dto.getCode());
        flow.setMsg(dto.getMsg());
        flow.setPassbackParams(dto.getPassback_params());
        flow.setPaySuccess(dto.isTradeSuccess());
        // 放入到map中
        Map<String, Object> transMap = new HashMap<>();
        transMap.put("payOrder",payOrder);
        transMap.put("flow",flow);
        // 发送事务消息
        log.info("异步通知开始事务消息");
        PayNotifyDto payNotifyDto = new PayNotifyDto(dto.getOut_trade_no(), payOrder.getExtParams());
        Message<String> message = MessageBuilder.withPayload(JSONObject.toJSONString(payNotifyDto)).build();
        // 发送mq事务消息
        TransactionSendResult sendResult = rocketMQTemplate.sendMessageInTransaction(
                "tx-alipay-group",
                "notify-topic:pay-tags",
                message,
                transMap // 本地执行事务需要的对象
        );
        SendStatus sendStatus = sendResult.getSendStatus();
        LocalTransactionState localTransactionState = sendResult.getLocalTransactionState();
        // 校验事务消息是否成功
        if(sendStatus != SendStatus.SEND_OK || localTransactionState != LocalTransactionState.COMMIT_MESSAGE) {
            throw new GlobleException("事务消息失败!","404");
        }
        log.info("支付异步通知事务消息结束{}",sendStatus);
        return "SUCCESS";

    }

    /**
     * 处理回调通知,修改支付单状态,添加支付流水
     * @param map
     */
    @Override
    public void handlerPayNotify(Map<String, Object> map) {
        AssertUtil.isNotNull(map,"参数异常!");
        PayOrder payOrder = (PayOrder)map.get("payOrder");
        PayFlow flow = (PayFlow)map.get("flow");
        // 查询数据校验幂等性
        PayOrder selectById = selectById(payOrder.getId());
        AssertUtil.isTrue(CommonConstants.OrderPayState.STATE_WAIT.equals(selectById.getPayStatus()),
                "状态异常!");
        // 保存
        log.info("修改支付单成功状态,保存支付流水");
        updateById(payOrder);
        payFlowService.insert(flow);
    }

    /**
     * 取消支付
     * @param jsonResult
     */
    @Override
    public void cancelPay(String jsonResult) {
        Map<String,String> map =  JSONObject.parseObject(jsonResult, Map.class);
        String orderNo = map.get("orderNo");
        PayOrder payOrder = checkPayOrder(orderNo);
        if (payOrder == null) {
            return; // 支付单不存在,直接结束
        }
        if(!CommonConstants.OrderPayState.STATE_WAIT.equals(payOrder.getPayStatus())){
            return; // 状态异常
        }
        payOrder.setPayStatus(CommonConstants.OrderPayState.STATE_CANCEL);
        payOrder.setUpdateTime(new Date());
        updateById(payOrder);
        try {
            // 调用支付宝关闭交易
           Factory.Payment.Common().close(orderNo);
           log.info("订单超时调用支付宝关闭交易!");
        } catch (Exception e) {
            e.printStackTrace();
            // 异常
        }

    }


}
